<commit_before>package golog

import (
	"fmt"
	"os"
	"strings"
	"time"
)

var (
	DEBUG = Level{
		value: 10,
		color: "blue",
		icon:  "★",
		Name:  "DEBUG",
	}

	INFO = Level{
		value: 20,
		color: "green",
		icon:  "♥",
		Name:  "INFO",
	}

	WARN = Level{
		value: 30,
		color: "yellow",
		icon:  "\u26A0",
		Name:  "WARN",
	}

	ERROR = Level{
		value: 40,
		color: "red",
		Name:  "ERROR",
		icon:  "✖",
	}

	PANIC = Level{
		value: 50,
		color: "black",
		icon:  "☹",
		Name:  "PANIC",
	}

	// limit when logger name will be normalized
	// normalized names are shown in console using stdout appender
	maxnamelen = 20
	curnamelen = 7

	// supported name separators
	separators []byte = []byte{'/', '.', '-'}
)

// Representing log level
type Level struct {
	// level priority value
	// bigger number has bigger priority
	value int

	// color which will used by stdout appender
	// github.com/ivpusic/go-clicolor
	color string

	// ascii icon of level
	icon string

	// level name
	Name string
}

// Representing one Log instance
type Log struct {
	// date and time of log
	Time time.Time `json:"time"`

	// logged message
	Message string `json:"message"`

	// log level
	Level Level `json:"level"`

	// additional data sent to log
	// this part should be handled by appenders
	// appender can decide to ignore data or to store it on specific way
	Data []interface{} `json:"data"`

	// id of process which made log
	Pid int `json:"pid"`

	// logger instance
	Logger *Logger `json:"logger"`
}

// Representing one logger instance
// Logger can have multiple appenders, it can enable it,
// or disable it. Also you can define level which will be specific to this logger.
type Logger struct {
	// list of appenders
	appenders []Appender

	// is logged disabled
	disabled bool

	// name of logger
	// logger name will be shown in stdout appender output
	// also it can be used to enable/disable logger
	Name string `json:"name"`

	// minimum level of log to be shown
	Level Level `json:"-"`

	// if this flag is set to true, in case any errors in appender
	// appender should panic. This also depends on appender implementation,
	// so appender can decide to ignore or to accept information in this flag
	DoPanic bool `json:"-"`
}

// Making and sending log entry to appenders if log level is appropriate.
func (l *Logger) makeLog(msg interface{}, lvl Level, data []interface{}) {
	if l.disabled {
		return
	}

	if lvl.value >= l.Level.value {
		log := Log{
			Time:    time.Now(),
			Message: l.toString(msg),
			Level:   lvl,
			Data:    data,
			Logger:  l,
			Pid:     os.Getpid(),
		}

		for _, appender := range l.appenders {
			appender.Append(log)
		}
	}
}

func (l *Logger) toString(object interface{}) string {
	return fmt.Sprintf("%v", object)
}

// method will normalize names if they are too big or too short
// normal name length if defined by namelen variable
func (l *Logger) normalizeName() {
	length := len(l.Name)

	// name is ok as it is
	if length == maxnamelen || length == curnamelen {
		return
	}

	// name is too short, add some spaces
	if length < curnamelen {
		l.normalizeNameLen()
		return
	}

	// name is too long
	// do best to normalize it

	var (
		normalized string
		parts      []string
		separator  byte
	)

	// try split long name using different separators
	// this first one which can split name into smaller parts will be used
	for _, sep := range separators {
		parts = strings.Split(l.Name, string(sep))
		if len(parts) > 1 {
			separator = sep
			break
		}
	}

	// if we sucesufully splitted string into multiple parts
	if len(parts) > 1 {
		appendSeparator := true

		for i, str := range parts {
			// if part length is bigger than zero
			switch len(str) {
			case 0:
				appendSeparator = false
				break
			case 1:
				normalized += str[:1]
				break
			case 2:
				normalized += str[:2]
				break
			default:
				normalized += str[:3]
				break
			}

			if appendSeparator && (i != (len(parts) - 1)) {
				normalized += string(separator)
			}
		}

		// if still to long
		if len(normalized) > maxnamelen {
			normalized = normalized[:maxnamelen]
		}
	} else {
		length := len(l.Name)
		if length > maxnamelen {
			normalized = l.Name[:maxnamelen]
		} else {
			normalized = l.Name[0:length]
		}
	}

	l.Name = normalized
	if len(normalized) >= curnamelen {
		curnamelen = len(normalized)
	} else {
		l.normalizeNameLen()
	}
}

// if name is still to short we will add spaces
func (l *Logger) normalizeNameLen() {
	length := len(l.Name)
	missing := curnamelen - length
	for i := 0; i < missing; i++ {
		l.Name += " "
	}
}

// Making log with DEBUG level.
func (l *Logger) Debug(msg interface{}, data ...interface{}) {
	l.makeLog(msg, DEBUG, data)
}

// Making log with INFO level.
func (l *Logger) Info(msg interface{}, data ...interface{}) {
	l.makeLog(msg, INFO, data)
}

// Making log with WARN level.
func (l *Logger) Warn(msg interface{}, data ...interface{}) {
	l.makeLog(msg, WARN, data)
}

// Making log with ERROR level.
func (l *Logger) Error(msg interface{}, data ...interface{}) {
	l.makeLog(msg, ERROR, data)
}

// Making log with PANIC level.
func (l *Logger) Panic(msg interface{}, data ...interface{}) {
	l.makeLog(msg, PANIC, data)
	panic(msg)
}

// Making formatted log with DEBUG level.
func (l *Logger) Debugf(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), DEBUG, nil)
}

// Making formatted log with INFO level.
func (l *Logger) Infof(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), INFO, nil)
}

// Making formatted log with WARN level.
func (l *Logger) Warnf(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), WARN, nil)
}

// Making formatted log with ERROR level.
func (l *Logger) Errorf(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), ERROR, nil)
}

// Making formatted log with PANIC level.
func (l *Logger) Panicf(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), PANIC, nil)
}

// When you want to send logs to another appender,
// you should create instance of appender and call this method.
// Method is expecting appender instance to be passed
// to this method. At the end passed appender will receive logs
func (l *Logger) Enable(appender Appender) {
	l.appenders = append(l.appenders, appender)
}

// If you want to disable logs from some appender you can use this method.
// You have to call method either with appender instance,
// or you can pass appender Id as argument.
// If appender is found, it will be removed from list of appenders of this logger,
// and all other further logs won't be received by this appender.
func (l *Logger) Disable(target interface{}) {
	var id string
	var appender Appender

	switch object := target.(type) {
	case string:
		id = object
	case Appender:
		appender = object
	default:
		l.Warn("Error while disabling logger. Cannot cast to target type.")
		return
	}

	for i, app := range l.appenders {
		// if we can find the same appender reference
		// or we can extract and match id from appender
		// or we can match received id string argument with one of appender's id
		if (appender != nil && (app == appender || appender.Id() == app.Id())) || id == app.Id() {
			var toAppend []Appender

			if len(l.appenders) >= i+1 {
				toAppend = l.appenders[i+1:]
			}

			l.appenders = append(l.appenders[:i], toAppend...)
			return
		}
	}
}
<commit_msg>Panicf fix<commit_after>package golog

import (
	"fmt"
	"os"
	"strings"
	"time"
)

var (
	DEBUG = Level{
		value: 10,
		color: "blue",
		icon:  "★",
		Name:  "DEBUG",
	}

	INFO = Level{
		value: 20,
		color: "green",
		icon:  "♥",
		Name:  "INFO",
	}

	WARN = Level{
		value: 30,
		color: "yellow",
		icon:  "\u26A0",
		Name:  "WARN",
	}

	ERROR = Level{
		value: 40,
		color: "red",
		Name:  "ERROR",
		icon:  "✖",
	}

	PANIC = Level{
		value: 50,
		color: "black",
		icon:  "☹",
		Name:  "PANIC",
	}

	// limit when logger name will be normalized
	// normalized names are shown in console using stdout appender
	maxnamelen = 20
	curnamelen = 7

	// supported name separators
	separators []byte = []byte{'/', '.', '-'}
)

// Representing log level
type Level struct {
	// level priority value
	// bigger number has bigger priority
	value int

	// color which will used by stdout appender
	// github.com/ivpusic/go-clicolor
	color string

	// ascii icon of level
	icon string

	// level name
	Name string
}

// Representing one Log instance
type Log struct {
	// date and time of log
	Time time.Time `json:"time"`

	// logged message
	Message string `json:"message"`

	// log level
	Level Level `json:"level"`

	// additional data sent to log
	// this part should be handled by appenders
	// appender can decide to ignore data or to store it on specific way
	Data []interface{} `json:"data"`

	// id of process which made log
	Pid int `json:"pid"`

	// logger instance
	Logger *Logger `json:"logger"`
}

// Representing one logger instance
// Logger can have multiple appenders, it can enable it,
// or disable it. Also you can define level which will be specific to this logger.
type Logger struct {
	// list of appenders
	appenders []Appender

	// is logged disabled
	disabled bool

	// name of logger
	// logger name will be shown in stdout appender output
	// also it can be used to enable/disable logger
	Name string `json:"name"`

	// minimum level of log to be shown
	Level Level `json:"-"`

	// if this flag is set to true, in case any errors in appender
	// appender should panic. This also depends on appender implementation,
	// so appender can decide to ignore or to accept information in this flag
	DoPanic bool `json:"-"`
}

// Making and sending log entry to appenders if log level is appropriate.
func (l *Logger) makeLog(msg interface{}, lvl Level, data []interface{}) {
	if l.disabled {
		return
	}

	if lvl.value >= l.Level.value {
		log := Log{
			Time:    time.Now(),
			Message: l.toString(msg),
			Level:   lvl,
			Data:    data,
			Logger:  l,
			Pid:     os.Getpid(),
		}

		for _, appender := range l.appenders {
			appender.Append(log)
		}
	}
}

func (l *Logger) toString(object interface{}) string {
	return fmt.Sprintf("%v", object)
}

// method will normalize names if they are too big or too short
// normal name length if defined by namelen variable
func (l *Logger) normalizeName() {
	length := len(l.Name)

	// name is ok as it is
	if length == maxnamelen || length == curnamelen {
		return
	}

	// name is too short, add some spaces
	if length < curnamelen {
		l.normalizeNameLen()
		return
	}

	// name is too long
	// do best to normalize it

	var (
		normalized string
		parts      []string
		separator  byte
	)

	// try split long name using different separators
	// this first one which can split name into smaller parts will be used
	for _, sep := range separators {
		parts = strings.Split(l.Name, string(sep))
		if len(parts) > 1 {
			separator = sep
			break
		}
	}

	// if we sucesufully splitted string into multiple parts
	if len(parts) > 1 {
		appendSeparator := true

		for i, str := range parts {
			// if part length is bigger than zero
			switch len(str) {
			case 0:
				appendSeparator = false
				break
			case 1:
				normalized += str[:1]
				break
			case 2:
				normalized += str[:2]
				break
			default:
				normalized += str[:3]
				break
			}

			if appendSeparator && (i != (len(parts) - 1)) {
				normalized += string(separator)
			}
		}

		// if still to long
		if len(normalized) > maxnamelen {
			normalized = normalized[:maxnamelen]
		}
	} else {
		length := len(l.Name)
		if length > maxnamelen {
			normalized = l.Name[:maxnamelen]
		} else {
			normalized = l.Name[0:length]
		}
	}

	l.Name = normalized
	if len(normalized) >= curnamelen {
		curnamelen = len(normalized)
	} else {
		l.normalizeNameLen()
	}
}

// if name is still to short we will add spaces
func (l *Logger) normalizeNameLen() {
	length := len(l.Name)
	missing := curnamelen - length
	for i := 0; i < missing; i++ {
		l.Name += " "
	}
}

// Making log with DEBUG level.
func (l *Logger) Debug(msg interface{}, data ...interface{}) {
	l.makeLog(msg, DEBUG, data)
}

// Making log with INFO level.
func (l *Logger) Info(msg interface{}, data ...interface{}) {
	l.makeLog(msg, INFO, data)
}

// Making log with WARN level.
func (l *Logger) Warn(msg interface{}, data ...interface{}) {
	l.makeLog(msg, WARN, data)
}

// Making log with ERROR level.
func (l *Logger) Error(msg interface{}, data ...interface{}) {
	l.makeLog(msg, ERROR, data)
}

// Making log with PANIC level.
func (l *Logger) Panic(msg interface{}, data ...interface{}) {
	l.makeLog(msg, PANIC, data)
	panic(msg)
}

// Making formatted log with DEBUG level.
func (l *Logger) Debugf(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), DEBUG, nil)
}

// Making formatted log with INFO level.
func (l *Logger) Infof(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), INFO, nil)
}

// Making formatted log with WARN level.
func (l *Logger) Warnf(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), WARN, nil)
}

// Making formatted log with ERROR level.
func (l *Logger) Errorf(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), ERROR, nil)
}

// Making formatted log with PANIC level.
func (l *Logger) Panicf(msg string, params ...interface{}) {
	l.makeLog(fmt.Sprintf(msg, params...), PANIC, nil)
	panic(msg)
}

// When you want to send logs to another appender,
// you should create instance of appender and call this method.
// Method is expecting appender instance to be passed
// to this method. At the end passed appender will receive logs
func (l *Logger) Enable(appender Appender) {
	l.appenders = append(l.appenders, appender)
}

// If you want to disable logs from some appender you can use this method.
// You have to call method either with appender instance,
// or you can pass appender Id as argument.
// If appender is found, it will be removed from list of appenders of this logger,
// and all other further logs won't be received by this appender.
func (l *Logger) Disable(target interface{}) {
	var id string
	var appender Appender

	switch object := target.(type) {
	case string:
		id = object
	case Appender:
		appender = object
	default:
		l.Warn("Error while disabling logger. Cannot cast to target type.")
		return
	}

	for i, app := range l.appenders {
		// if we can find the same appender reference
		// or we can extract and match id from appender
		// or we can match received id string argument with one of appender's id
		if (appender != nil && (app == appender || appender.Id() == app.Id())) || id == app.Id() {
			var toAppend []Appender

			if len(l.appenders) >= i+1 {
				toAppend = l.appenders[i+1:]
			}

			l.appenders = append(l.appenders[:i], toAppend...)
			return
		}
	}
}
<|endoftext|>


<commit_before>package yiigo

import (
	"fmt"
	"sync"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

var (
	// Logger default logger
	Logger *zap.Logger
	logMap sync.Map
)

type logOptions struct {
	maxSize    int
	maxAge     int
	maxBackups int
	compress   bool
	debug      bool
}

// LogOption configures how we set up the logger
type LogOption interface {
	apply(options *logOptions)
}

// funcLogOption implements db option
type funcLogOption struct {
	f func(options *logOptions)
}

func (fo *funcLogOption) apply(o *logOptions) {
	fo.f(o)
}

func newFuncLogOption(f func(options *logOptions)) *funcLogOption {
	return &funcLogOption{f: f}
}

// WithLogMaxSize specifies the `MaxSize` to logger.
// MaxSize is the maximum size in megabytes of the log file before it gets
// rotated. It defaults to 100 megabytes.
func WithLogMaxSize(n int) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.maxSize = n
	})
}

// WithLogMaxAge specifies the `MaxAge` to logger.
// MaxAge is the maximum number of days to retain old log files based on the
// timestamp encoded in their filename.  Note that a day is defined as 24
// hours and may not exactly correspond to calendar days due to daylight
// savings, leap seconds, etc. The default is not to remove old log files
// based on age.
func WithLogMaxAge(n int) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.maxAge = n
	})
}

// WithLogMaxBackups specifies the `MaxBackups` to logger.
// MaxBackups is the maximum number of old log files to retain.  The default
// is to retain all old log files (though MaxAge may still cause them to get
// deleted.)
func WithLogMaxBackups(n int) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.maxBackups = n
	})
}

// WithLogCompress specifies the `Compress` to logger.
// Compress determines if the rotated log files should be compressed
// using gzip.
func WithLogCompress(b bool) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.compress = b
	})
}

// WithLogDebug specifies the `Debug` mode to logger.
func WithLogDebug(b bool) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.debug = b
	})
}

// initLogger init logger, the default `MaxSize` is 500M.
func initLogger(logfile string, options ...LogOption) *zap.Logger {
	o := &logOptions{maxSize: 500}

	if len(options) > 0 {
		for _, option := range options {
			option.apply(o)
		}
	}

	if o.debug {
		cfg := zap.NewDevelopmentConfig()

		cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
		cfg.EncoderConfig.EncodeTime = MyTimeEncoder

		l, _ := cfg.Build()

		return l
	}

	w := zapcore.AddSync(&lumberjack.Logger{
		Filename:   logfile,
		MaxSize:    o.maxSize,
		MaxBackups: o.maxBackups,
		MaxAge:     o.maxAge,
		Compress:   o.compress,
	})

	cfg := zap.NewProductionEncoderConfig()

	cfg.TimeKey = "time"
	cfg.EncodeTime = MyTimeEncoder
	cfg.EncodeCaller = zapcore.FullCallerEncoder

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(cfg),
		w,
		zap.DebugLevel,
	)

	return zap.New(core, zap.AddCaller())
}

// RegisterLogger register logger
func RegisterLogger(name, file string, options ...LogOption) {
	logger := initLogger(file, options...)

	logMap.Store(name, logger)

	if name == AsDefault {
		Logger = logger
	}
}

// UseLogger returns a logger
func UseLogger(name string) *zap.Logger {
	v, ok := logMap.Load(name)

	if !ok {
		panic(fmt.Errorf("yiigo: logger.%s is not registered", name))
	}

	return v.(*zap.Logger)
}

// MyTimeEncoder zap time encoder.
func MyTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05"))
}
<commit_msg>update logger<commit_after>package yiigo

import (
	"fmt"
	"sync"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

var (
	// Logger default logger
	Logger *zap.Logger
	logMap sync.Map
)

type logOptions struct {
	maxSize    int
	maxAge     int
	maxBackups int
	compress   bool
	debug      bool
}

// LogOption configures how we set up the logger
type LogOption interface {
	apply(options *logOptions)
}

// funcLogOption implements db option
type funcLogOption struct {
	f func(options *logOptions)
}

func (fo *funcLogOption) apply(o *logOptions) {
	fo.f(o)
}

func newFuncLogOption(f func(options *logOptions)) *funcLogOption {
	return &funcLogOption{f: f}
}

// WithLogMaxSize specifies the `MaxSize` to logger.
// MaxSize is the maximum size in megabytes of the log file before it gets
// rotated. It defaults to 100 megabytes.
func WithLogMaxSize(n int) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.maxSize = n
	})
}

// WithLogMaxAge specifies the `MaxAge` to logger.
// MaxAge is the maximum number of days to retain old log files based on the
// timestamp encoded in their filename.  Note that a day is defined as 24
// hours and may not exactly correspond to calendar days due to daylight
// savings, leap seconds, etc. The default is not to remove old log files
// based on age.
func WithLogMaxAge(n int) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.maxAge = n
	})
}

// WithLogMaxBackups specifies the `MaxBackups` to logger.
// MaxBackups is the maximum number of old log files to retain.  The default
// is to retain all old log files (though MaxAge may still cause them to get
// deleted.)
func WithLogMaxBackups(n int) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.maxBackups = n
	})
}

// WithLogCompress specifies the `Compress` to logger.
// Compress determines if the rotated log files should be compressed
// using gzip.
func WithLogCompress(b bool) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.compress = b
	})
}

// WithLogDebug specifies the `Debug` mode to logger.
func WithLogDebug(b bool) LogOption {
	return newFuncLogOption(func(o *logOptions) {
		o.debug = b
	})
}

// initLogger init logger, the default `MaxSize` is 500M.
func initLogger(logfile string, options ...LogOption) *zap.Logger {
	o := &logOptions{maxSize: 500}

	if len(options) > 0 {
		for _, option := range options {
			option.apply(o)
		}
	}

	if o.debug {
		cfg := zap.NewDevelopmentConfig()

		cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
		cfg.EncoderConfig.EncodeTime = MyTimeEncoder

		l, _ := cfg.Build()

		return l
	}

	w := zapcore.AddSync(&lumberjack.Logger{
		Filename:   logfile,
		MaxSize:    o.maxSize,
		MaxBackups: o.maxBackups,
		MaxAge:     o.maxAge,
		Compress:   o.compress,
	})

	cfg := zap.NewProductionEncoderConfig()

	cfg.TimeKey = "time"
	cfg.EncodeTime = MyTimeEncoder
	cfg.EncodeCaller = zapcore.FullCallerEncoder

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(cfg),
		w,
		zap.DebugLevel,
	)

	return zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
}

// RegisterLogger register logger
func RegisterLogger(name, file string, options ...LogOption) {
	logger := initLogger(file, options...)

	logMap.Store(name, logger)

	if name == AsDefault {
		Logger = logger
	}
}

// UseLogger returns a logger
func UseLogger(name string) *zap.Logger {
	v, ok := logMap.Load(name)

	if !ok {
		panic(fmt.Errorf("yiigo: logger.%s is not registered", name))
	}

	return v.(*zap.Logger)
}

// MyTimeEncoder zap time encoder.
func MyTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05"))
}
<|endoftext|>


<commit_before>package veneur

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"testing"
	"time"

	lightstep "github.com/lightstep/lightstep-tracer-go"
	"github.com/stretchr/testify/assert"
	"github.com/stripe/veneur/samplers"
)

func TestServerTags(t *testing.T) {
	metrics := []samplers.DDMetric{{
		Name:       "foo.bar.baz",
		Value:      [1][2]float64{{float64(time.Now().Unix()), float64(1.0)}},
		Tags:       []string{"gorch:frobble", "x:e"},
		MetricType: "rate",
		Interval:   10,
	}}

	finalizeMetrics("somehostname", []string{"a:b", "c:d"}, metrics)
	assert.Equal(t, "somehostname", metrics[0].Hostname, "Metric hostname uses argument")
	assert.Contains(t, metrics[0].Tags, "a:b", "Tags should contain server tags")
}

func TestHostMagicTag(t *testing.T) {
	metrics := []samplers.DDMetric{{
		Name:       "foo.bar.baz",
		Value:      [1][2]float64{{float64(time.Now().Unix()), float64(1.0)}},
		Tags:       []string{"gorch:frobble", "host:abc123", "x:e"},
		MetricType: "rate",
		Interval:   10,
	}}

	finalizeMetrics("badhostname", []string{"a:b", "c:d"}, metrics)
	assert.Equal(t, "abc123", metrics[0].Hostname, "Metric hostname should be from tag")
	assert.NotContains(t, metrics[0].Tags, "host:abc123", "Host tag should be removed")
	assert.Contains(t, metrics[0].Tags, "x:e", "Last tag is still around")
}

func TestDeviceMagicTag(t *testing.T) {
	metrics := []samplers.DDMetric{{
		Name:       "foo.bar.baz",
		Value:      [1][2]float64{{float64(time.Now().Unix()), float64(1.0)}},
		Tags:       []string{"gorch:frobble", "device:abc123", "x:e"},
		MetricType: "rate",
		Interval:   10,
	}}

	finalizeMetrics("badhostname", []string{"a:b", "c:d"}, metrics)
	assert.Equal(t, "abc123", metrics[0].DeviceName, "Metric devicename should be from tag")
	assert.NotContains(t, metrics[0].Tags, "device:abc123", "Host tag should be removed")
	assert.Contains(t, metrics[0].Tags, "x:e", "Last tag is still around")
}

func TestFlushTracesDatadog(t *testing.T) {
	type TestCase struct {
		Name         string
		ProtobufFile string
		JSONFile     string
	}

	cases := []TestCase{
		{
			Name:         "Success",
			ProtobufFile: filepath.Join("fixtures", "protobuf", "trace.pb"),
			JSONFile:     filepath.Join("fixtures", "tracing_agent", "spans", "trace.pb.json"),
		},
		{
			Name:         "Critical",
			ProtobufFile: filepath.Join("fixtures", "protobuf", "trace_critical.pb"),
			JSONFile:     filepath.Join("fixtures", "tracing_agent", "spans", "trace_critical.pb.json"),
		},
	}

	for _, tc := range cases {
		t.Run(tc.Name, func(t *testing.T) {
			pb, err := os.Open(tc.ProtobufFile)
			assert.NoError(t, err)
			defer pb.Close()

			js, err := os.Open(tc.JSONFile)
			assert.NoError(t, err)
			defer js.Close()

			testFlushTraceDatadog(t, pb, js)
		})
	}

	for _, tc := range cases {
		t.Run(fmt.Sprintf("%s_%s", tc.Name, "lightstep"), func(t *testing.T) {
			pb, err := os.Open(tc.ProtobufFile)
			assert.NoError(t, err)
			defer pb.Close()

			js, err := os.Open(tc.JSONFile)
			assert.NoError(t, err)
			defer js.Close()

			testFlushTraceLightstep(t, pb, js)
		})
	}
}

func testFlushTraceDatadog(t *testing.T, protobuf, jsn io.Reader) {
	remoteResponseChan := make(chan struct{}, 1)
	remoteServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var expected []*DatadogTraceSpan
		err := json.NewDecoder(jsn).Decode(&expected)
		assert.NoError(t, err)

		var actual []*DatadogTraceSpan
		err = json.NewDecoder(r.Body).Decode(&actual)
		assert.NoError(t, err)

		assert.Equal(t, expected, actual)

		w.WriteHeader(http.StatusAccepted)

		remoteResponseChan <- struct{}{}
	}))
	defer remoteServer.Close()

	config := globalConfig()
	config.TraceAPIAddress = remoteServer.URL

	server := setupVeneurServer(t, config, nil)
	defer server.Shutdown()

	server.tracerSinks = append(server.tracerSinks, tracerSink{
		name:   "Datadog",
		tracer: nil,
		flush:  flushSpansDatadog,
	})

	assert.Equal(t, server.DDTraceAddress, config.TraceAPIAddress)

	packet, err := ioutil.ReadAll(protobuf)
	assert.NoError(t, err)

	server.HandleTracePacket(packet)

	assert.NoError(t, err)
	server.Flush()

	// wait for remoteServer to process the POST
	select {
	case <-remoteResponseChan:
		// all is safe
		break
	case <-time.After(10 * time.Second):
		assert.Fail(t, "Global server did not complete all responses before test terminated!")
	}
}

// testFlushTraceLightstep tests that the Lightstep sink can be initialized correctly
// and that the flushSpansLightstep function executes without error.
// We can't actually test the functionality end-to-end because the lightstep
// implementation doesn't expose itself for mocking.
func testFlushTraceLightstep(t *testing.T, protobuf, jsn io.Reader) {
	config := globalConfig()

	// this can be anything as long as it's not empty
	config.TraceAPIAddress = "http://example.org"
	server := setupVeneurServer(t, config, nil)
	defer server.Shutdown()

	lightstepTracer := lightstep.NewTracer(lightstep.Options{
		AccessToken: "TestAccessToken",
	})

	server.tracerSinks = append(server.tracerSinks, tracerSink{
		name:   "Lightstep",
		tracer: lightstepTracer,
		flush:  flushSpansLightstep,
	})

	assert.Equal(t, server.DDTraceAddress, config.TraceAPIAddress)

	packet, err := ioutil.ReadAll(protobuf)
	assert.NoError(t, err)

	server.HandleTracePacket(packet)

	assert.NoError(t, err)
	server.Flush()
}
<commit_msg>flusher_test.go: TestFlushTracesDatadog: Fix occasional test failures<commit_after>package veneur

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"testing"
	"time"

	lightstep "github.com/lightstep/lightstep-tracer-go"
	"github.com/stretchr/testify/assert"
	"github.com/stripe/veneur/samplers"
)

func TestServerTags(t *testing.T) {
	metrics := []samplers.DDMetric{{
		Name:       "foo.bar.baz",
		Value:      [1][2]float64{{float64(time.Now().Unix()), float64(1.0)}},
		Tags:       []string{"gorch:frobble", "x:e"},
		MetricType: "rate",
		Interval:   10,
	}}

	finalizeMetrics("somehostname", []string{"a:b", "c:d"}, metrics)
	assert.Equal(t, "somehostname", metrics[0].Hostname, "Metric hostname uses argument")
	assert.Contains(t, metrics[0].Tags, "a:b", "Tags should contain server tags")
}

func TestHostMagicTag(t *testing.T) {
	metrics := []samplers.DDMetric{{
		Name:       "foo.bar.baz",
		Value:      [1][2]float64{{float64(time.Now().Unix()), float64(1.0)}},
		Tags:       []string{"gorch:frobble", "host:abc123", "x:e"},
		MetricType: "rate",
		Interval:   10,
	}}

	finalizeMetrics("badhostname", []string{"a:b", "c:d"}, metrics)
	assert.Equal(t, "abc123", metrics[0].Hostname, "Metric hostname should be from tag")
	assert.NotContains(t, metrics[0].Tags, "host:abc123", "Host tag should be removed")
	assert.Contains(t, metrics[0].Tags, "x:e", "Last tag is still around")
}

func TestDeviceMagicTag(t *testing.T) {
	metrics := []samplers.DDMetric{{
		Name:       "foo.bar.baz",
		Value:      [1][2]float64{{float64(time.Now().Unix()), float64(1.0)}},
		Tags:       []string{"gorch:frobble", "device:abc123", "x:e"},
		MetricType: "rate",
		Interval:   10,
	}}

	finalizeMetrics("badhostname", []string{"a:b", "c:d"}, metrics)
	assert.Equal(t, "abc123", metrics[0].DeviceName, "Metric devicename should be from tag")
	assert.NotContains(t, metrics[0].Tags, "device:abc123", "Host tag should be removed")
	assert.Contains(t, metrics[0].Tags, "x:e", "Last tag is still around")
}

func TestFlushTracesDatadog(t *testing.T) {
	type TestCase struct {
		Name         string
		ProtobufFile string
		JSONFile     string
	}

	cases := []TestCase{
		{
			Name:         "Success",
			ProtobufFile: filepath.Join("fixtures", "protobuf", "trace.pb"),
			JSONFile:     filepath.Join("fixtures", "tracing_agent", "spans", "trace.pb.json"),
		},
		{
			Name:         "Critical",
			ProtobufFile: filepath.Join("fixtures", "protobuf", "trace_critical.pb"),
			JSONFile:     filepath.Join("fixtures", "tracing_agent", "spans", "trace_critical.pb.json"),
		},
	}

	for _, tc := range cases {
		t.Run(tc.Name, func(t *testing.T) {
			pb, err := os.Open(tc.ProtobufFile)
			assert.NoError(t, err)
			defer pb.Close()

			js, err := os.Open(tc.JSONFile)
			assert.NoError(t, err)
			defer js.Close()

			testFlushTraceDatadog(t, pb, js)
		})
	}

	for _, tc := range cases {
		t.Run(fmt.Sprintf("%s_%s", tc.Name, "lightstep"), func(t *testing.T) {
			pb, err := os.Open(tc.ProtobufFile)
			assert.NoError(t, err)
			defer pb.Close()

			js, err := os.Open(tc.JSONFile)
			assert.NoError(t, err)
			defer js.Close()

			testFlushTraceLightstep(t, pb, js)
		})
	}
}

func testFlushTraceDatadog(t *testing.T, protobuf, jsn io.Reader) {
	var expected []*DatadogTraceSpan
	err := json.NewDecoder(jsn).Decode(&expected)
	assert.NoError(t, err)

	remoteResponseChan := make(chan []*DatadogTraceSpan, 1)
	remoteServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var actual []*DatadogTraceSpan
		err = json.NewDecoder(r.Body).Decode(&actual)
		assert.NoError(t, err)

		w.WriteHeader(http.StatusAccepted)
		remoteResponseChan <- actual
	}))
	defer remoteServer.Close()

	config := globalConfig()
	config.TraceAPIAddress = remoteServer.URL

	server := setupVeneurServer(t, config, nil)
	defer server.Shutdown()

	server.tracerSinks = append(server.tracerSinks, tracerSink{
		name:   "Datadog",
		tracer: nil,
		flush:  flushSpansDatadog,
	})

	assert.Equal(t, server.DDTraceAddress, config.TraceAPIAddress)

	packet, err := ioutil.ReadAll(protobuf)
	assert.NoError(t, err)

	server.HandleTracePacket(packet)
	server.Flush()

	// wait for remoteServer to process the POST
	select {
	case actual := <-remoteResponseChan:
		assert.Equal(t, expected, actual)
		// all is safe
		break
	case <-time.After(10 * time.Second):
		assert.Fail(t, "Global server did not complete all responses before test terminated!")
	}
}

// testFlushTraceLightstep tests that the Lightstep sink can be initialized correctly
// and that the flushSpansLightstep function executes without error.
// We can't actually test the functionality end-to-end because the lightstep
// implementation doesn't expose itself for mocking.
func testFlushTraceLightstep(t *testing.T, protobuf, jsn io.Reader) {
	config := globalConfig()

	// this can be anything as long as it's not empty
	config.TraceAPIAddress = "http://example.org"
	server := setupVeneurServer(t, config, nil)
	defer server.Shutdown()

	lightstepTracer := lightstep.NewTracer(lightstep.Options{
		AccessToken: "TestAccessToken",
	})

	server.tracerSinks = append(server.tracerSinks, tracerSink{
		name:   "Lightstep",
		tracer: lightstepTracer,
		flush:  flushSpansLightstep,
	})

	assert.Equal(t, server.DDTraceAddress, config.TraceAPIAddress)

	packet, err := ioutil.ReadAll(protobuf)
	assert.NoError(t, err)

	server.HandleTracePacket(packet)

	assert.NoError(t, err)
	server.Flush()
}
<|endoftext|>


<commit_before>package postgres

import (
	"bytes"
	"database/sql"
	"github.com/Hackform/Eiffel/service/kappa"
	"github.com/Hackform/Eiffel/service/repo"
	"github.com/Hackform/Eiffel/service/repo/q"
	"strconv"
)

/////////////////
// Transaction //
/////////////////

type (
	tx struct {
		t *sql.Tx
	}
)

func newTx(t *sql.Tx) *tx {
	return &tx{
		t: t,
	}
}

func (t *tx) Statement(q.Q) (repo.Stmt, error) {
	return nil, nil
}

func (t *tx) Commit() error {
	return nil
}

func (t *tx) Rollback() error {
	return nil
}

func parseQuery(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("SELECT ")
	l := len(qu.RProps) - 1
	for n, i := range qu.RProps {
		query.WriteString(i)
		if n < l {
			query.WriteString(", ")
		}
	}
	query.WriteString(" FROM " + qu.Sector)
	if qu.Cons != nil {
		query.WriteString(" WHERE ")
		k := kappa.New()
		query.WriteString(parseConstraints(qu.Cons, k))
	}
	if qu.Action == q.ACTION_QUERY_MULTI {
		if qu.Order != nil {
			query.WriteString(" ORDER BY ")
			l := len(qu.Order) - 1
			for n, i := range qu.Order {
				query.WriteString(i.Key)
				switch i.Condition {
				case q.ASC:
					query.WriteString(" ASC")
				case q.DESC:
					query.WriteString(" DESC")
				}
				if n < l {
					query.WriteString(", ")
				}
			}
		}
		query.WriteString(" LIMIT " + strconv.Itoa(qu.Limit))
	}
	query.WriteString(";")
	return query.String()
}

func parseInsert(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("INSERT INTO " + qu.Sector + " (")
	l := len(qu.RProps) - 1
	for n, i := range qu.RProps {
		query.WriteString(i)
		if n < l {
			query.WriteString(", ")
		}
	}
	query.WriteString(") VALUES (")
	k := kappa.New()
	for n, i := range qu.Vals {
		if i == escape_sequence {
			query.WriteString(escape_sequence + strconv.Itoa(k.Get()))
		} else {
			query.WriteString(i)
		}
		if n < l {
			query.WriteString(", ")
		}
	}
	query.WriteString(");")
	return query.String()
}

func parseUpdate(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("UPDATE " + qu.Sector + " SET ")
	k := kappa.New()
	l := len(qu.Mods) - 1
	for n, i := range qu.Mods {
		query.WriteString(i.Key + " = ")
		if i.Value == escape_sequence {
			query.WriteString(escape_sequence + strconv.Itoa(k.Get()))
		} else {
			query.WriteString(i.Value)
		}
		if n < l {
			query.WriteString(", ")
		}
	}
	if qu.Cons != nil {
		query.WriteString(" WHERE ")
		query.WriteString(parseConstraints(qu.Cons, k))
	}
	query.WriteString(";")
	return query.String()
}

func parseDelete(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("DELETE FROM " + qu.Sector)
	if qu.Cons != nil {
		k := kappa.New()
		query.WriteString(" WHERE ")
		query.WriteString(parseConstraints(qu.Cons, k))
	}
	query.WriteString(";")
	return query.String()
}

func parseT(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("CREATE TABLE " + qu.Sector + " (")
	l := len(qu.Cons) - 1
	for n, i := range qu.Cons {
		query.WriteString(i.Key)
		switch i.Condition {
		case q.BOOLEAN:
			query.WriteString(" BOOLEAN")
		case q.UUID:
			query.WriteString(" UUID")
		case q.VARCHAR:
			query.WriteString(" VARCHAR(" + strconv.Itoa(i.Size) + ")")
		case q.CHAR:
			query.WriteString(" CHAR(" + strconv.Itoa(i.Size) + ")")
		case q.TEXT:
			query.WriteString(" TEXT")
		case q.SMALLINT:
			query.WriteString(" SMALLINT")
		case q.INT:
			query.WriteString(" INT")
		case q.BIGINT:
			query.WriteString(" BIGINT")
		case q.REAL:
			query.WriteString(" REAL")
		case q.DOUBLE:
			query.WriteString(" DOUBLE")
		case q.TIMESTAMP:
			query.WriteString(" TIMESTAMP")
		}
		switch i.ColCon {
		case q.NONE:
		case q.NOT_NULL:
			query.WriteString(" NOT NULL")
		case q.UNIQUE:
			query.WriteString(" UNIQUE")
		case q.NOT_NULL_UNIQUE:
			query.WriteString(" NOT NULL UNIQUE")
		case q.PRIMARY:
			query.WriteString(" PRIMARY KEY")
		}
		if n < l {
			query.WriteString(", ")
		}
	}
	query.WriteString(");")
	return query.String()
}

func parseQ(qu q.Q) string {
	switch qu.Action {
	case q.ACTION_QUERY_ONE, q.ACTION_QUERY_MULTI:
		return parseQuery(qu)
	case q.ACTION_INSERT:
		return parseInsert(qu)
	case q.ACTION_UPDATE:
		return parseUpdate(qu)
	case q.ACTION_DELETE:
		return parseDelete(qu)
	case q.ACTION_CREATE_TABLE:
		return parseT(qu)
	}
	return ""
}

func parseConstraints(cons q.Constraints, k *kappa.Kappa) string {
	l := len(cons) - 1
	clause := bytes.Buffer{}
	for n, i := range cons {
		c := i.Condition
		if c != q.AND && c != q.OR {
			clause.WriteString(i.Key)
		}

		switch c {
		case q.EQUAL:
			clause.WriteString(" = ")
		case q.UNEQUAL:
			clause.WriteString(" <> ")
		case q.GREATER:
			clause.WriteString(" > ")
		case q.LESSER:
			clause.WriteString(" < ")
		case q.GREATER_EQ:
			clause.WriteString(" >= ")
		case q.LESSER_EQ:
			clause.WriteString(" <= ")

		case q.AND:
			clause.WriteString("(" + parseConstraints(q.Constraints{i.Con1}, k) + " AND " + parseConstraints(q.Constraints{i.Con2}, k) + ")")
		case q.OR:
			clause.WriteString("(" + parseConstraints(q.Constraints{i.Con1}, k) + " OR " + parseConstraints(q.Constraints{i.Con2}, k) + ")")
		}

		if c != q.AND && c != q.OR {
			if i.Value == escape_sequence {
				clause.WriteString(escape_sequence + strconv.Itoa(k.Get()))
			} else {
				clause.WriteString(i.Value)
			}
		}

		if n < l {
			clause.WriteString(" AND ")
		}
	}
	return clause.String()
}
<commit_msg>transaction returns statement<commit_after>package postgres

import (
	"bytes"
	"database/sql"
	"github.com/Hackform/Eiffel/service/kappa"
	"github.com/Hackform/Eiffel/service/repo"
	"github.com/Hackform/Eiffel/service/repo/q"
	"strconv"
)

/////////////////
// Transaction //
/////////////////

type (
	tx struct {
		t *sql.Tx
	}
)

func newTx(t *sql.Tx) *tx {
	return &tx{
		t: t,
	}
}

func (t *tx) Statement(query q.Q) (repo.Stmt, error) {
	stmt, err := t.t.Prepare(parseQ(query))
	return newStmt(stmt), err
}

func (t *tx) Commit() error {
	return t.t.Commit()
}

func (t *tx) Rollback() error {
	return t.t.Rollback()
}

func parseQuery(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("SELECT ")
	l := len(qu.RProps) - 1
	for n, i := range qu.RProps {
		query.WriteString(i)
		if n < l {
			query.WriteString(", ")
		}
	}
	query.WriteString(" FROM " + qu.Sector)
	if qu.Cons != nil {
		query.WriteString(" WHERE ")
		k := kappa.New()
		query.WriteString(parseConstraints(qu.Cons, k))
	}
	if qu.Action == q.ACTION_QUERY_MULTI {
		if qu.Order != nil {
			query.WriteString(" ORDER BY ")
			l := len(qu.Order) - 1
			for n, i := range qu.Order {
				query.WriteString(i.Key)
				switch i.Condition {
				case q.ASC:
					query.WriteString(" ASC")
				case q.DESC:
					query.WriteString(" DESC")
				}
				if n < l {
					query.WriteString(", ")
				}
			}
		}
		query.WriteString(" LIMIT " + strconv.Itoa(qu.Limit))
	}
	query.WriteString(";")
	return query.String()
}

func parseInsert(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("INSERT INTO " + qu.Sector + " (")
	l := len(qu.RProps) - 1
	for n, i := range qu.RProps {
		query.WriteString(i)
		if n < l {
			query.WriteString(", ")
		}
	}
	query.WriteString(") VALUES (")
	k := kappa.New()
	for n, i := range qu.Vals {
		if i == escape_sequence {
			query.WriteString(escape_sequence + strconv.Itoa(k.Get()))
		} else {
			query.WriteString(i)
		}
		if n < l {
			query.WriteString(", ")
		}
	}
	query.WriteString(");")
	return query.String()
}

func parseUpdate(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("UPDATE " + qu.Sector + " SET ")
	k := kappa.New()
	l := len(qu.Mods) - 1
	for n, i := range qu.Mods {
		query.WriteString(i.Key + " = ")
		if i.Value == escape_sequence {
			query.WriteString(escape_sequence + strconv.Itoa(k.Get()))
		} else {
			query.WriteString(i.Value)
		}
		if n < l {
			query.WriteString(", ")
		}
	}
	if qu.Cons != nil {
		query.WriteString(" WHERE ")
		query.WriteString(parseConstraints(qu.Cons, k))
	}
	query.WriteString(";")
	return query.String()
}

func parseDelete(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("DELETE FROM " + qu.Sector)
	if qu.Cons != nil {
		k := kappa.New()
		query.WriteString(" WHERE ")
		query.WriteString(parseConstraints(qu.Cons, k))
	}
	query.WriteString(";")
	return query.String()
}

func parseT(qu q.Q) string {
	query := bytes.Buffer{}
	query.WriteString("CREATE TABLE " + qu.Sector + " (")
	l := len(qu.Cons) - 1
	for n, i := range qu.Cons {
		query.WriteString(i.Key)
		switch i.Condition {
		case q.BOOLEAN:
			query.WriteString(" BOOLEAN")
		case q.UUID:
			query.WriteString(" UUID")
		case q.VARCHAR:
			query.WriteString(" VARCHAR(" + strconv.Itoa(i.Size) + ")")
		case q.CHAR:
			query.WriteString(" CHAR(" + strconv.Itoa(i.Size) + ")")
		case q.TEXT:
			query.WriteString(" TEXT")
		case q.SMALLINT:
			query.WriteString(" SMALLINT")
		case q.INT:
			query.WriteString(" INT")
		case q.BIGINT:
			query.WriteString(" BIGINT")
		case q.REAL:
			query.WriteString(" REAL")
		case q.DOUBLE:
			query.WriteString(" DOUBLE")
		case q.TIMESTAMP:
			query.WriteString(" TIMESTAMP")
		}
		switch i.ColCon {
		case q.NONE:
		case q.NOT_NULL:
			query.WriteString(" NOT NULL")
		case q.UNIQUE:
			query.WriteString(" UNIQUE")
		case q.NOT_NULL_UNIQUE:
			query.WriteString(" NOT NULL UNIQUE")
		case q.PRIMARY:
			query.WriteString(" PRIMARY KEY")
		}
		if n < l {
			query.WriteString(", ")
		}
	}
	query.WriteString(");")
	return query.String()
}

func parseQ(qu q.Q) string {
	switch qu.Action {
	case q.ACTION_QUERY_ONE, q.ACTION_QUERY_MULTI:
		return parseQuery(qu)
	case q.ACTION_INSERT:
		return parseInsert(qu)
	case q.ACTION_UPDATE:
		return parseUpdate(qu)
	case q.ACTION_DELETE:
		return parseDelete(qu)
	case q.ACTION_CREATE_TABLE:
		return parseT(qu)
	}
	return ""
}

func parseConstraints(cons q.Constraints, k *kappa.Kappa) string {
	l := len(cons) - 1
	clause := bytes.Buffer{}
	for n, i := range cons {
		c := i.Condition
		if c != q.AND && c != q.OR {
			clause.WriteString(i.Key)
		}

		switch c {
		case q.EQUAL:
			clause.WriteString(" = ")
		case q.UNEQUAL:
			clause.WriteString(" <> ")
		case q.GREATER:
			clause.WriteString(" > ")
		case q.LESSER:
			clause.WriteString(" < ")
		case q.GREATER_EQ:
			clause.WriteString(" >= ")
		case q.LESSER_EQ:
			clause.WriteString(" <= ")

		case q.AND:
			clause.WriteString("(" + parseConstraints(q.Constraints{i.Con1}, k) + " AND " + parseConstraints(q.Constraints{i.Con2}, k) + ")")
		case q.OR:
			clause.WriteString("(" + parseConstraints(q.Constraints{i.Con1}, k) + " OR " + parseConstraints(q.Constraints{i.Con2}, k) + ")")
		}

		if c != q.AND && c != q.OR {
			if i.Value == escape_sequence {
				clause.WriteString(escape_sequence + strconv.Itoa(k.Get()))
			} else {
				clause.WriteString(i.Value)
			}
		}

		if n < l {
			clause.WriteString(" AND ")
		}
	}
	return clause.String()
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"

	pb "github.com/pachyderm/pachyderm/src/client/version/versionpb"
)

const (
	// MajorVersion is the current major version for pachyderm.
	MajorVersion = 1
	// MinorVersion is the current minor version for pachyderm.
	MinorVersion = 7
	// MicroVersion is the patch number for pachyderm.
	MicroVersion = 1
)

var (
	// AdditionalVersion is the string provided at release time
	// The value is passed to the linker at build time
	//
	// DO NOT set the value of this variable here. For some reason, if
	// AdditionalVersion is set here, the go linker will not overwrite it.
	AdditionalVersion string

	// Version is the current version for pachyderm.
	Version = &pb.Version{
		Major:      MajorVersion,
		Minor:      MinorVersion,
		Micro:      MicroVersion,
		Additional: AdditionalVersion,
	}
)

// PrettyPrintVersion returns a version string optionally tagged with metadata.
// For example: "1.2.3", or "1.2.3rc1" if version.Additional is "rc1".
func PrettyPrintVersion(version *pb.Version) string {
	result := PrettyPrintVersionNoAdditional(version)
	if version.Additional != "" {
		result += fmt.Sprintf("%s", version.Additional)
	}
	return result
}

// PrettyVersion calls PrettyPrintVersion on Version and returns the result.
func PrettyVersion() string {
	return PrettyPrintVersion(Version)
}

// PrettyPrintVersionNoAdditional returns a version string without
// version.Additional.
func PrettyPrintVersionNoAdditional(version *pb.Version) string {
	return fmt.Sprintf("%d.%d.%d", version.Major, version.Minor, version.Micro)
}
<commit_msg>Bump verison to 1.7.2<commit_after>package version

import (
	"fmt"

	pb "github.com/pachyderm/pachyderm/src/client/version/versionpb"
)

const (
	// MajorVersion is the current major version for pachyderm.
	MajorVersion = 1
	// MinorVersion is the current minor version for pachyderm.
	MinorVersion = 7
	// MicroVersion is the patch number for pachyderm.
	MicroVersion = 2
)

var (
	// AdditionalVersion is the string provided at release time
	// The value is passed to the linker at build time
	//
	// DO NOT set the value of this variable here. For some reason, if
	// AdditionalVersion is set here, the go linker will not overwrite it.
	AdditionalVersion string

	// Version is the current version for pachyderm.
	Version = &pb.Version{
		Major:      MajorVersion,
		Minor:      MinorVersion,
		Micro:      MicroVersion,
		Additional: AdditionalVersion,
	}
)

// PrettyPrintVersion returns a version string optionally tagged with metadata.
// For example: "1.2.3", or "1.2.3rc1" if version.Additional is "rc1".
func PrettyPrintVersion(version *pb.Version) string {
	result := PrettyPrintVersionNoAdditional(version)
	if version.Additional != "" {
		result += fmt.Sprintf("%s", version.Additional)
	}
	return result
}

// PrettyVersion calls PrettyPrintVersion on Version and returns the result.
func PrettyVersion() string {
	return PrettyPrintVersion(Version)
}

// PrettyPrintVersionNoAdditional returns a version string without
// version.Additional.
func PrettyPrintVersionNoAdditional(version *pb.Version) string {
	return fmt.Sprintf("%d.%d.%d", version.Major, version.Minor, version.Micro)
}
<|endoftext|>


<commit_before>package logtap

// Overview

import (
  "reflect"
  "time"
)

type Logger interface {
  Fatal(string, ...interface{})
  Error(string, ...interface{})
  Warn(string, ...interface{})
  Info(string, ...interface{})
  Debug(string, ...interface{})
  Trace(string, ...interface{})
}

type DevNullLogger int8
func (d DevNullLogger) Fatal(thing string,v ...interface{}) {}
func (d DevNullLogger) Error(thing string,v ...interface{}) {}
func (d DevNullLogger) Warn(thing string,v ...interface{}) {}
func (d DevNullLogger) Info(thing string,v ...interface{}) {}
func (d DevNullLogger) Debug(thing string,v ...interface{}) {}
func (d DevNullLogger) Trace(thing string,v ...interface{}) {}

type Collector interface {
  CollectChan() chan Message
  SetLogger(Logger)
}

type Drain interface {
  Write(Message)
  SetLogger(Logger)
}

type Message struct {
  Time     time.Time
  Priority int
  Content  string
}

type Logtap struct {
  log Logger
  Collectors map[string]Collector
  Drains map[string]Drain
}

// Establishes a new logtap object
// and makes sure it has the some logger
func New(log Logger) *Logtap {
  if log == nil {
    log = DevNullLogger(0)
  }
  return &Logtap{
    log: log,
    Collectors: make(map[string]Collector),
    Drains: make(map[string]Drain),
  }
}

// AddDrain addes a drain to the listeners and sets its logger
func (l *Logtap) AddDrain(tag string, d Drain) {
  d.SetLogger(l.log)
  l.Drains[tag] = d
}

// RemoveDrain drops a drain
func (l *Logtap) RemoveDrain(tag string) {
  delete(l.Drains, tag)
}

// AddCollector adds a collector and begins listening to it
// also adds logging
func (l *Logtap) AddCollector(tag string, c Collector) {
  c.SetLogger(l.log)
  l.Collectors[tag] = c
}

// RemoveCollector remove given collector
func (l *Logtap) RemoveCollector(tag string) {
  delete(l.Collectors, tag)
}

// Start begins listening to all the collectors that are registered.
// it then broadcasts all messages to all the drains that are registered
// this is backgrounded so it can be used by a parent process without getting in the way
func (l *Logtap) Start() {
  go func() {
    for {
      cases := make([]reflect.SelectCase, len(l.Collectors))
      for _, col := range l.Collectors {
        cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(col.CollectChan())})
      }
      // when you append to a nil value [] the first element is the nil value object
      // so its best to remove it
      cases = cases[1:]
      _, value, ok := reflect.Select(cases)
      // ok will be true if the channel has not been closed.
      if ok {
        l.log.Info("[start][collect] %v", value.Interface().(Message))
        l.writeMessage(value.Interface().(Message))
      }

    }

  }()
}

// writeMessage broadcasts to all drains in seperate go routines
func (l *Logtap) writeMessage(msg Message) {
  for _, drain := range l.Drains {
    go drain.Write(msg)
  }
}

<commit_msg>remove the devnull logger<commit_after>package logtap

// Overview

import (
  "reflect"
  "time"
  "errors"
)

type Logger interface {
  Fatal(string, ...interface{})
  Error(string, ...interface{})
  Warn(string, ...interface{})
  Info(string, ...interface{})
  Debug(string, ...interface{})
  Trace(string, ...interface{})
}

type Collector interface {
  CollectChan() chan Message
  SetLogger(Logger)
}

type Drain interface {
  Write(Message)
  SetLogger(Logger)
}

type Message struct {
  Time     time.Time
  Priority int
  Content  string
}

type Logtap struct {
  log Logger
  Collectors map[string]Collector
  Drains map[string]Drain
}

// Establishes a new logtap object
// and makes sure it has the some logger
func New(log Logger) *Logtap, error {
  if log == nil {
    return nil, errors.New("Cannot create a new Logtap without a logger")
  }
  return &Logtap{
    log: log,
    Collectors: make(map[string]Collector),
    Drains: make(map[string]Drain),
  }
}

// AddDrain addes a drain to the listeners and sets its logger
func (l *Logtap) AddDrain(tag string, d Drain) {
  d.SetLogger(l.log)
  l.Drains[tag] = d
}

// RemoveDrain drops a drain
func (l *Logtap) RemoveDrain(tag string) {
  delete(l.Drains, tag)
}

// AddCollector adds a collector and begins listening to it
// also adds logging
func (l *Logtap) AddCollector(tag string, c Collector) {
  c.SetLogger(l.log)
  l.Collectors[tag] = c
}

// RemoveCollector remove given collector
func (l *Logtap) RemoveCollector(tag string) {
  delete(l.Collectors, tag)
}

// Start begins listening to all the collectors that are registered.
// it then broadcasts all messages to all the drains that are registered
// this is backgrounded so it can be used by a parent process without getting in the way
func (l *Logtap) Start() {
  go func() {
    for {
      cases := make([]reflect.SelectCase, len(l.Collectors))
      for _, col := range l.Collectors {
        cases = append(cases, reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(col.CollectChan())})
      }
      // when you append to a nil value [] the first element is the nil value object
      // so its best to remove it
      cases = cases[1:]
      _, value, ok := reflect.Select(cases)
      // ok will be true if the channel has not been closed.
      if ok {
        l.log.Info("[start][collect] %v", value.Interface().(Message))
        l.writeMessage(value.Interface().(Message))
      }

    }

  }()
}

// writeMessage broadcasts to all drains in seperate go routines
func (l *Logtap) writeMessage(msg Message) {
  for _, drain := range l.Drains {
    go drain.Write(msg)
  }
}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/miekg/dns"
	"net"
)

var dnsClient = &dns.Client{}

// Query the given nameserver for all domains
func resolveDomains(nameserver string) (results resultMap, err error) {
	results = make(resultMap)

	for _, domain := range domains {
		result, err := resolve(nameserver, domain)
		if err != nil {
			return nil, err
		}
		results[domain] = result
	}

	return results, nil
}

// Query the given nameserver for a single domain
func resolve(nameserver string, domain string) (records stringSet, err error) {
	m := &dns.Msg{}
	m.RecursionDesired = true
	m.SetQuestion(dns.Fqdn(domain), dns.TypeA)

	hostPort := net.JoinHostPort(nameserver, "53")
	attempt := 1
	result := &dns.Msg{}

	// execute the query
	for {
		result, _, err = dnsClient.Exchange(m, hostPort)
		if err == nil {
			// success
			break
		} else {
			err = simplifyError(err)
			if err.Error() != "i/o timeout" || attempt == maxAttempts {
				// network problem or timeout
				return nil, simplifyError(err)
			} else {
				// retry
				attempt++
			}
		}
	}

	// NXDomain rcode?
	if result.Rcode == dns.RcodeNameError {
		return nil, nil
	}

	// Other erroneous rcode?
	if result.Rcode != dns.RcodeSuccess {
		return nil, fmt.Errorf("%v for %s", dns.RcodeToString[result.Rcode], domain)
	}

	records = make(stringSet)

	// Add addresses to set
	for _, a := range result.Answer {
		if record, ok := a.(*dns.A); ok {
			records.add(record.A.String())
		}
	}

	return records, nil
}

func ptrName(address string) string {
	reverse, err := dns.ReverseAddr(address)
	if err != nil {
		return ""
	}

	m := &dns.Msg{}
	m.RecursionDesired = true
	m.SetQuestion(reverse, dns.TypePTR)

	// execute the query
	result, _, err := dnsClient.Exchange(m, net.JoinHostPort(referenceServer, "53"))
	if result == nil || result.Rcode != dns.RcodeSuccess {
		return ""
	}

	// Add addresses to set
	for _, a := range result.Answer {
		if record, ok := a.(*dns.PTR); ok {
			return record.Ptr
		}
	}
	return ""
}

// Query a nameserver for the bind version
func version(address string) string {
	m := &dns.Msg{}
	m.RecursionDesired = true
	m.Question = make([]dns.Question, 1)
	m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS}

	// Execute the query
	r, _, _ := dnsClient.Exchange(m, net.JoinHostPort(address, "53"))

	// Valid response?
	if r != nil && r.Rcode == dns.RcodeSuccess {
		for _, a := range r.Answer {
			if record, ok := a.(*dns.TXT); ok {
				return record.Txt[0]
			}
		}
	}
	return ""
}
<commit_msg>Clean up return statements<commit_after>package main

import (
	"fmt"
	"github.com/miekg/dns"
	"net"
)

var dnsClient = &dns.Client{}

// Query the given nameserver for all domains
func resolveDomains(nameserver string) (results resultMap, err error) {
	results = make(resultMap)

	for _, domain := range domains {
		result, err := resolve(nameserver, domain)
		if err != nil {
			return nil, err
		}
		results[domain] = result
	}

	return results, nil
}

// Query the given nameserver for a single domain
func resolve(nameserver string, domain string) (records stringSet, err error) {
	m := &dns.Msg{}
	m.RecursionDesired = true
	m.SetQuestion(dns.Fqdn(domain), dns.TypeA)

	hostPort := net.JoinHostPort(nameserver, "53")
	attempt := 1
	result := &dns.Msg{}

	// execute the query
	for {
		result, _, err = dnsClient.Exchange(m, hostPort)
		if err == nil {
			// success
			break
		} else {
			err = simplifyError(err)
			if err.Error() != "i/o timeout" || attempt == maxAttempts {
				// network problem or timeout
				return
			} else {
				err = nil
				// retry
				attempt++
			}
		}
	}

	// NXDomain rcode?
	if result.Rcode == dns.RcodeNameError {
		return
	}

	// Other erroneous rcode?
	if result.Rcode != dns.RcodeSuccess {
		err = fmt.Errorf("%v for %s", dns.RcodeToString[result.Rcode], domain)
		return
	}

	records = make(stringSet)

	// Add addresses to set
	for _, a := range result.Answer {
		if record, ok := a.(*dns.A); ok {
			records.add(record.A.String())
		}
	}

	return
}

func ptrName(address string) string {
	reverse, err := dns.ReverseAddr(address)
	if err != nil {
		return ""
	}

	m := &dns.Msg{}
	m.RecursionDesired = true
	m.SetQuestion(reverse, dns.TypePTR)

	// execute the query
	result, _, err := dnsClient.Exchange(m, net.JoinHostPort(referenceServer, "53"))
	if result == nil || result.Rcode != dns.RcodeSuccess {
		return ""
	}

	// Add addresses to set
	for _, a := range result.Answer {
		if record, ok := a.(*dns.PTR); ok {
			return record.Ptr
		}
	}
	return ""
}

// Query a nameserver for the bind version
func version(address string) string {
	m := &dns.Msg{}
	m.RecursionDesired = true
	m.Question = make([]dns.Question, 1)
	m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS}

	// Execute the query
	r, _, _ := dnsClient.Exchange(m, net.JoinHostPort(address, "53"))

	// Valid response?
	if r != nil && r.Rcode == dns.RcodeSuccess {
		for _, a := range r.Answer {
			if record, ok := a.(*dns.TXT); ok {
				return record.Txt[0]
			}
		}
	}
	return ""
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/sendgrid/sendgrid-go"
	"log"
)

type mailer struct {
	client *sendgrid.SGClient
}

type Mailer interface {
	SendNotification(channel *Channel, datapoint *Datapoint) error
}

func NewMailer(username string, password string) Mailer {
	sg := sendgrid.NewSendGridClient(username, password)
	return &mailer{sg}
}

func (mailer *mailer) SendNotification(channel *Channel, datapoint *Datapoint) error {
	if len(channel.Email) == 0 {
		return nil
	}
	log.Printf("Sending notification to %v", channel.Email)

	// TODO: Stop hardcoding.
	subject := fmt.Sprintf("[beerserver] Over limit: %f/26.0 @%s", datapoint.Value, channel.Name)
	text := fmt.Sprintf("Hurry up and cool down your beer!")

	mail := sendgrid.NewMail()
	mail.AddTo(channel.Email)
	mail.SetSubject(subject)
	mail.SetText(text)
	mail.SetFrom("notification@beerserver.local")

	err := mailer.client.Send(mail)
	if err != nil {
		log.Printf("Failed to send mail to %v", channel.Email)
	}
	return err
}
<commit_msg>Log detailed information.<commit_after>package main

import (
	"fmt"
	"github.com/sendgrid/sendgrid-go"
	"log"
)

type mailer struct {
	client *sendgrid.SGClient
}

type Mailer interface {
	SendNotification(channel *Channel, datapoint *Datapoint) error
}

func NewMailer(username string, password string) Mailer {
	sg := sendgrid.NewSendGridClient(username, password)
	return &mailer{sg}
}

func (mailer *mailer) SendNotification(channel *Channel, datapoint *Datapoint) error {
	if len(channel.Email) == 0 {
		log.Printf("No notification address for channel %v", channel.Name)
		return nil
	}
	log.Printf("Sending notification for %v to %v", channel.Name, channel.Email)

	// TODO: Stop hardcoding.
	subject := fmt.Sprintf("[beerserver] Over limit: %f/26.0 @%s", datapoint.Value, channel.Name)
	text := fmt.Sprintf("Hurry up and cool down your beer!")

	mail := sendgrid.NewMail()
	mail.AddTo(channel.Email)
	mail.SetSubject(subject)
	mail.SetText(text)
	mail.SetFrom("notification@beerserver.local")

	err := mailer.client.Send(mail)
	if err != nil {
		log.Printf("Failed to send mail to %v", channel.Email)
	}
	return err
}
<|endoftext|>


<commit_before>package linalg

import (
	"errors"
	"strconv"
)

type Matrix struct {
	row, col int
	data     []complex128
}

func (M *Matrix) String() string {
	ret := ""
	for i := 0; i < M.row; i++ {
		for j := 0; j < M.col; j++ {
			ret += strconv.FormatFloat(real(M.Elem(i, j)), 'g', -1, 64) + " "
			ret += strconv.FormatFloat(imag(M.Elem(i, j)), 'g', -1, 64) + "j"
			if j < M.col-1 {
				ret += ", "
			}
		}
		ret += "\n"
	}
	return ret
}

/* Access Functions */
func (M *Matrix) Elem(row, col int) complex128 {
	return M.data[row+col*M.row]
}

func (M *Matrix) Set_elem(row, col int, val complex128) {
	M.data[row+col*M.row] = val
}

func (M *Matrix) Set_int(row, col, val int) {
	M.Set_elem(row, col, complex(float64(val), 0))
}

func (M *Matrix) Set_float(row, col int, val float64) {
	M.Set_elem(row, col, complex(val, 0))
}

/* Creation Functions */
func Ident(dim int) *Matrix {
	var mat = new(Matrix)
	mat.row = dim
	mat.col = dim
	mat.data = make([]complex128, dim*dim)
	for i := 0; i < dim; i++ {
		mat.Set_elem(i, i, 1)
	}

	return mat
}

func Zero(row, col int) *Matrix {
	var M = new(Matrix)
	M.row = row
	M.col = col
	M.data = make([]complex128, row*col)
	return M
}

func (M *Matrix) Copy() *Matrix {
	var N = Zero(M.col, M.row)
	copy(N.data, M.data)

	return N
}

func Init(row, col int, slice []complex128) *Matrix {
	var M = Zero(row, col)
	M.data = slice
	return M
}

/* Math Functions */
func (M *Matrix) Eq(N *Matrix) bool {
	if (M.row != N.row) || (M.col != N.col) {
		return false
	}
	for i := 0; i < M.col; i++ {
		for j := 0; j < M.row; j++ {
			if M.Elem(i, j) != N.Elem(i, j) {
				return false
			}
		}
	}
	return true
}

func Mul(A, B, C *Matrix) error {
	if A.col != B.row || A.row != C.row || B.col != C.col {
		return errors.New("Matrix Multiplication size mismatch")
	}

	for i := 0; i < C.row; i++ {
		for j := 0; j < C.col; j++ {
			var accum complex128
			for k := 0; k < A.col; k++ {
				accum += A.Elem(i, k) * B.Elem(k, j)
			}
			C.Set_elem(i, j, accum)
		}
	}
	return nil
}

func Add(A, B, C *Matrix) error {
	if A.col != B.col || A.col != C.col || A.row != B.row || A.row != C.row {
		return errors.New("Matrix Addition size mismatch")
	}

	for i := 0; i < A.col; i++ {
		for j := 0; j < A.row; j++ {
			C.Set_elem(i, j, A.Elem(i, j)+B.Elem(i, j))
		}
	}
	return nil
}

func (M *Matrix) Scale(scalar complex128) {
	for i := 0; i < M.col*M.row; i++ {
		M.data[i] *= scalar
	}
}
<commit_msg>Adds subtraction<commit_after>package linalg

import (
	"errors"
	"strconv"
)

type Matrix struct {
	row, col int
	data     []complex128
}

func (M *Matrix) String() string {
	ret := ""
	for i := 0; i < M.row; i++ {
		for j := 0; j < M.col; j++ {
			ret += strconv.FormatFloat(real(M.Elem(i, j)), 'g', -1, 64) + " "
			ret += strconv.FormatFloat(imag(M.Elem(i, j)), 'g', -1, 64) + "j"
			if j < M.col-1 {
				ret += ", "
			}
		}
		ret += "\n"
	}
	return ret
}

/* Access Functions */
func (M *Matrix) Elem(row, col int) complex128 {
	return M.data[row+col*M.row]
}

func (M *Matrix) Set_elem(row, col int, val complex128) {
	M.data[row+col*M.row] = val
}

func (M *Matrix) Set_int(row, col, val int) {
	M.Set_elem(row, col, complex(float64(val), 0))
}

func (M *Matrix) Set_float(row, col int, val float64) {
	M.Set_elem(row, col, complex(val, 0))
}

/* Creation Functions */
func Ident(dim int) *Matrix {
	var mat = new(Matrix)
	mat.row = dim
	mat.col = dim
	mat.data = make([]complex128, dim*dim)
	for i := 0; i < dim; i++ {
		mat.Set_elem(i, i, 1)
	}

	return mat
}

func Zero(row, col int) *Matrix {
	var M = new(Matrix)
	M.row = row
	M.col = col
	M.data = make([]complex128, row*col)
	return M
}

func (M *Matrix) Copy() *Matrix {
	var N = Zero(M.col, M.row)
	copy(N.data, M.data)

	return N
}

func Init(row, col int, slice []complex128) *Matrix {
	var M = Zero(row, col)
	M.data = slice
	return M
}

/* Math Functions */
func (M *Matrix) Eq(N *Matrix) bool {
	if (M.row != N.row) || (M.col != N.col) {
		return false
	}
	for i := 0; i < M.col; i++ {
		for j := 0; j < M.row; j++ {
			if M.Elem(i, j) != N.Elem(i, j) {
				return false
			}
		}
	}
	return true
}

func Mul(A, B, C *Matrix) error {
	if A.col != B.row || A.row != C.row || B.col != C.col {
		return errors.New("Matrix Multiplication size mismatch")
	}

	for i := 0; i < C.row; i++ {
		for j := 0; j < C.col; j++ {
			var accum complex128
			for k := 0; k < A.col; k++ {
				accum += A.Elem(i, k) * B.Elem(k, j)
			}
			C.Set_elem(i, j, accum)
		}
	}
	return nil
}

func Add(A, B, C *Matrix) error {
	if A.col != B.col || A.col != C.col || A.row != B.row || A.row != C.row {
		return errors.New("Matrix Addition size mismatch")
	}

	for i := 0; i < A.col; i++ {
		for j := 0; j < A.row; j++ {
			C.Set_elem(i, j, A.Elem(i, j)+B.Elem(i, j))
		}
	}
	return nil
}

/*
	Calculates A-B and stores the result in C
*/
func Sub(A, B, C *Matrix) error {
	if A.col != B.col || A.col != C.col || A.row != B.row || A.row != C.row {
		return errors.New("Matrix Subtraction size mismatch")
	}

	for i := 0; i < A.col; i++ {
		for j := 0; j < A.row; j++ {
			C.Set_elem(i, j, A.Elem(i, j)-B.Elem(i, j))
		}
	}
	return nil
}

func (M *Matrix) Scale(scalar complex128) {
	for i := 0; i < M.col*M.row; i++ {
		M.data[i] *= scalar
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/gob"
	"fmt"
	"io"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"io/ioutil"
)

// DBClient provides access to cache, http client and configuration
type DBClient struct {
	cache   Cache
	http    *http.Client
	cfg     *Configuration
	counter *CounterByMode
}

// request holds structure for request
type request struct {
	details requestDetails
}

var emptyResp = &http.Response{}

// requestDetails stores information about request, it's used for creating unique hash and also as a payload structure
type requestDetails struct {
	Path        string              `json:"path"`
	Method      string              `json:"method"`
	Destination string              `json:"destination"`
	Scheme      string              `json:"scheme"`
	Query       string              `json:"query"`
	Body        string              `json:"body"`
	RemoteAddr  string              `json:"remoteAddr"`
	Headers     map[string][]string `json:"headers"`
}

func (r *request) concatenate() string {
	var buffer bytes.Buffer

	buffer.WriteString(r.details.Destination)
	buffer.WriteString(r.details.Path)
	buffer.WriteString(r.details.Method)
	buffer.WriteString(r.details.Query)
	buffer.WriteString(r.details.Body)

	return buffer.String()
}

// hash returns unique hash key for request
func (r *request) hash() string {
	h := md5.New()
	io.WriteString(h, r.concatenate())
	return fmt.Sprintf("%x", h.Sum(nil))
}

// res structure hold response body from external service, body is not decoded and is supposed
// to be bytes, however headers should provide all required information for later decoding
// by the client.
type response struct {
	Status  int                 `json:"status"`
	Body    string              `json:"body"`
	Headers map[string][]string `json:"headers"`
}

// Payload structure holds request and response structure
type Payload struct {
	Response response       `json:"response"`
	Request  requestDetails `json:"request"`
	ID       string         `json:"id"`
}

// encode method encodes all exported Payload fields to bytes
func (p *Payload) encode() ([]byte, error) {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	err := enc.Encode(p)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// decodePayload decodes supplied bytes into Payload structure
func decodePayload(data []byte) (*Payload, error) {
	var p *Payload
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	err := dec.Decode(&p)
	if err != nil {
		return nil, err
	}
	return p, nil
}

// captureRequest saves request for later playback
func (d *DBClient) captureRequest(req *http.Request) (*http.Response, error) {

	// this is mainly for testing, since when you create
	if req.Body == nil {
		req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	reqBody, err := ioutil.ReadAll(req.Body)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
			"mode":  "capture",
		}).Error("Got error when reading request body")
	}

	// outputting request body if verbose logging is set
	log.WithFields(log.Fields{
		"body": string(reqBody),
		"mode": "capture",
	}).Debug("got request body")

	req.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))

	// forwarding request
	resp, err := d.doRequest(req)

	if err == nil {
		respBody, err := extractBody(resp)

		if err != nil {

			log.WithFields(log.Fields{
				"error": err.Error(),
				"mode":  "capture",
			}).Error("Failed to copy response body.")

			return resp, err
		}

		// saving response body with request/response meta to cache
		d.save(req, reqBody, resp, respBody)
	}

	// return new response or error here
	return resp, err
}

func copyBody(body io.ReadCloser) (resp1, resp2 io.ReadCloser, err error) {
	var buf bytes.Buffer
	if _, err = buf.ReadFrom(body); err != nil {
		return nil, nil, err
	}
	if err = body.Close(); err != nil {
		return nil, nil, err
	}
	return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
}

func extractBody(resp *http.Response) (extract []byte, err error) {
	save := resp.Body
	savecl := resp.ContentLength

	save, resp.Body, err = copyBody(resp.Body)

	if err != nil {
		return
	}
	defer resp.Body.Close()
	extract, err = ioutil.ReadAll(resp.Body)

	resp.Body = save
	resp.ContentLength = savecl
	if err != nil {
		return nil, err
	}
	return extract, nil
}

// doRequest performs original request and returns response that should be returned to client and error (if there is one)
func (d *DBClient) doRequest(request *http.Request) (*http.Response, error) {

	// We can't have this set. And it only contains "/pkg/net/http/" anyway
	request.RequestURI = ""

	if d.cfg.middleware != "" {
		var payload Payload

		c := NewConstructor(request, payload)
		err := c.ApplyMiddleware(d.cfg.middleware)

		if err != nil {
			log.WithFields(log.Fields{
				"mode":   d.cfg.mode,
				"error":  err.Error(),
				"host":   request.Host,
				"method": request.Method,
				"path":   request.URL.Path,
			}).Error("could not forward request, middleware failed to modify request.")
			return nil, err
		}

		request, err = c.reconstructRequest()
		if err != nil {
			return nil, err
		}
	}

	resp, err := d.http.Do(request)

	if err != nil {
		log.WithFields(log.Fields{
			"mode":   d.cfg.mode,
			"error":  err.Error(),
			"host":   request.Host,
			"method": request.Method,
			"path":   request.URL.Path,
		}).Error("could not forward request, failed to do an HTTP request.")
		return nil, err
	}

	log.WithFields(log.Fields{
		"mode":   d.cfg.mode,
		"host":   request.Host,
		"method": request.Method,
		"path":   request.URL.Path,
	}).Debug("response from external service got successfuly!")

	resp.Header.Set("hoverfly", "Was-Here")
	return resp, nil

}

// save gets request fingerprint, extracts request body, status code and headers, then saves it to cache
func (d *DBClient) save(req *http.Request, reqBody []byte, resp *http.Response, respBody []byte) {
	// record request here
	key := getRequestFingerprint(req, reqBody)

	if resp == nil {
		resp = emptyResp
	} else {
		responseObj := response{
			Status:  resp.StatusCode,
			Body:    string(respBody),
			Headers: resp.Header,
		}

		log.WithFields(log.Fields{
			"path":          req.URL.Path,
			"rawQuery":      req.URL.RawQuery,
			"requestMethod": req.Method,
			"bodyLen":       len(reqBody),
			"destination":   req.Host,
			"hashKey":       key,
		}).Debug("Capturing")

		requestObj := requestDetails{
			Path:        req.URL.Path,
			Method:      req.Method,
			Destination: req.Host,
			Scheme:      req.URL.Scheme,
			Query:       req.URL.RawQuery,
			Body:        string(reqBody),
			RemoteAddr:  req.RemoteAddr,
			Headers:     req.Header,
		}

		payload := Payload{
			Response: responseObj,
			Request:  requestObj,
			ID:       key,
		}

		bts, err := payload.encode()
		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error("Failed to serialize payload")
		} else {
			d.cache.Set([]byte(key), bts)
		}
	}
}

// getRequestFingerprint returns request hash
func getRequestFingerprint(req *http.Request, requestBody []byte) string {
	details := requestDetails{
		Path:        req.URL.Path,
		Method:      req.Method,
		Destination: req.Host,
		Query:       req.URL.RawQuery,
		Body:        string(requestBody),
	}

	r := request{details: details}
	return r.hash()
}

// getResponse returns stored response from cache
func (d *DBClient) getResponse(req *http.Request) *http.Response {

	if req.Body == nil {
		req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	reqBody, err := ioutil.ReadAll(req.Body)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error when reading request body")
	}

	key := getRequestFingerprint(req, reqBody)

	payloadBts, err := d.cache.Get([]byte(key))

	if err == nil {
		// getting cache response
		payload, err := decodePayload(payloadBts)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
				"value": string(payloadBts),
				"key":   key,
			}).Error("Failed to decode payload")
			return hoverflyError(req, err, "Failed to virtualize", http.StatusInternalServerError)
		}

		c := NewConstructor(req, *payload)

		if d.cfg.middleware != "" {
			_ = c.ApplyMiddleware(d.cfg.middleware)
		}

		response := c.reconstructResponse()

		log.WithFields(log.Fields{
			"key":         key,
			"mode":        "virtualize",
			"middleware":  d.cfg.middleware,
			"path":        req.URL.Path,
			"rawQuery":    req.URL.RawQuery,
			"method":      req.Method,
			"destination": req.Host,
			"status":      payload.Response.Status,
			"bodyLength":  response.ContentLength,
		}).Info("Response found, returning")

		return response

	}

	log.WithFields(log.Fields{
		"key":         key,
		"error":       err.Error(),
		"query":       req.URL.RawQuery,
		"path":        req.URL.RawPath,
		"destination": req.Host,
		"method":      req.Method,
	}).Warn("Failed to retrieve response from cache")
	// return error? if we return nil - proxy forwards request to original destination
	return hoverflyError(req, err, "Could not find recorded request, please record it first!", http.StatusPreconditionFailed)
}

// modifyRequestResponse modifies outgoing request and then modifies incoming response, neither request nor response
// is saved to cache.
func (d *DBClient) modifyRequestResponse(req *http.Request, middleware string) (*http.Response, error) {

	// modifying request
	resp, err := d.doRequest(req)

	if err != nil {
		return nil, err
	}

	// preparing payload
	bodyBytes, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		log.WithFields(log.Fields{
			"error":      err.Error(),
			"middleware": middleware,
		}).Error("Failed to read response body after sending modified request")
		return nil, err
	}

	r := response{
		Status:  resp.StatusCode,
		Body:    string(bodyBytes),
		Headers: resp.Header,
	}
	payload := Payload{Response: r}

	c := NewConstructor(req, payload)
	// applying middleware to modify response
	err = c.ApplyMiddleware(middleware)

	if err != nil {
		return nil, err
	}

	newResponse := c.reconstructResponse()

	log.WithFields(log.Fields{
		"status":      newResponse.StatusCode,
		"middleware":  middleware,
		"mode":        "modify",
		"path":        c.payload.Request.Path,
		"rawQuery":    c.payload.Request.Query,
		"method":      c.payload.Request.Method,
		"destination": c.payload.Request.Destination,
		// original here
		"originalPath":        req.URL.Path,
		"originalRawQuery":    req.URL.RawQuery,
		"originalMethod":      req.Method,
		"originalDestination": req.Host,
	}).Info("request and response modified, returning")

	return newResponse, nil

}
<commit_msg>passing request details to middleware<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/gob"
	"fmt"
	"io"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"io/ioutil"
)

// DBClient provides access to cache, http client and configuration
type DBClient struct {
	cache   Cache
	http    *http.Client
	cfg     *Configuration
	counter *CounterByMode
}

// request holds structure for request
type request struct {
	details requestDetails
}

var emptyResp = &http.Response{}

// requestDetails stores information about request, it's used for creating unique hash and also as a payload structure
type requestDetails struct {
	Path        string              `json:"path"`
	Method      string              `json:"method"`
	Destination string              `json:"destination"`
	Scheme      string              `json:"scheme"`
	Query       string              `json:"query"`
	Body        string              `json:"body"`
	RemoteAddr  string              `json:"remoteAddr"`
	Headers     map[string][]string `json:"headers"`
}

func (r *request) concatenate() string {
	var buffer bytes.Buffer

	buffer.WriteString(r.details.Destination)
	buffer.WriteString(r.details.Path)
	buffer.WriteString(r.details.Method)
	buffer.WriteString(r.details.Query)
	buffer.WriteString(r.details.Body)

	return buffer.String()
}

// hash returns unique hash key for request
func (r *request) hash() string {
	h := md5.New()
	io.WriteString(h, r.concatenate())
	return fmt.Sprintf("%x", h.Sum(nil))
}

// res structure hold response body from external service, body is not decoded and is supposed
// to be bytes, however headers should provide all required information for later decoding
// by the client.
type response struct {
	Status  int                 `json:"status"`
	Body    string              `json:"body"`
	Headers map[string][]string `json:"headers"`
}

// Payload structure holds request and response structure
type Payload struct {
	Response response       `json:"response"`
	Request  requestDetails `json:"request"`
	ID       string         `json:"id"`
}

// encode method encodes all exported Payload fields to bytes
func (p *Payload) encode() ([]byte, error) {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	err := enc.Encode(p)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// decodePayload decodes supplied bytes into Payload structure
func decodePayload(data []byte) (*Payload, error) {
	var p *Payload
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	err := dec.Decode(&p)
	if err != nil {
		return nil, err
	}
	return p, nil
}

// captureRequest saves request for later playback
func (d *DBClient) captureRequest(req *http.Request) (*http.Response, error) {

	// this is mainly for testing, since when you create
	if req.Body == nil {
		req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	reqBody, err := ioutil.ReadAll(req.Body)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
			"mode":  "capture",
		}).Error("Got error when reading request body")
	}

	// outputting request body if verbose logging is set
	log.WithFields(log.Fields{
		"body": string(reqBody),
		"mode": "capture",
	}).Debug("got request body")

	req.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))

	// forwarding request
	resp, err := d.doRequest(req)

	if err == nil {
		respBody, err := extractBody(resp)

		if err != nil {

			log.WithFields(log.Fields{
				"error": err.Error(),
				"mode":  "capture",
			}).Error("Failed to copy response body.")

			return resp, err
		}

		// saving response body with request/response meta to cache
		d.save(req, reqBody, resp, respBody)
	}

	// return new response or error here
	return resp, err
}

func copyBody(body io.ReadCloser) (resp1, resp2 io.ReadCloser, err error) {
	var buf bytes.Buffer
	if _, err = buf.ReadFrom(body); err != nil {
		return nil, nil, err
	}
	if err = body.Close(); err != nil {
		return nil, nil, err
	}
	return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
}

func extractBody(resp *http.Response) (extract []byte, err error) {
	save := resp.Body
	savecl := resp.ContentLength

	save, resp.Body, err = copyBody(resp.Body)

	if err != nil {
		return
	}
	defer resp.Body.Close()
	extract, err = ioutil.ReadAll(resp.Body)

	resp.Body = save
	resp.ContentLength = savecl
	if err != nil {
		return nil, err
	}
	return extract, nil
}

// getRequestDetails - extracts request details, reads request body so it will be empty.
func getRequestDetails(req *http.Request) (requestObj requestDetails, err error) {
	if req.Body == nil {
		req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	reqBody, err := ioutil.ReadAll(req.Body)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
			"mode":  "capture",
		}).Error("Got error while reading request body")
		return
	}

	requestObj = requestDetails{
		Path:        req.URL.Path,
		Method:      req.Method,
		Destination: req.Host,
		Scheme:      req.URL.Scheme,
		Query:       req.URL.RawQuery,
		Body:        string(reqBody),
		RemoteAddr:  req.RemoteAddr,
		Headers:     req.Header,
	}
	return
}

// doRequest performs original request and returns response that should be returned to client and error (if there is one)
func (d *DBClient) doRequest(request *http.Request) (*http.Response, error) {

	// We can't have this set. And it only contains "/pkg/net/http/" anyway
	request.RequestURI = ""

	if d.cfg.middleware != "" {
		// middleware is provided, modifying request
		var payload Payload

		rd, err := getRequestDetails(request)
		if err != nil {
			return nil, err
		}
		payload.Request = rd

		c := NewConstructor(request, payload)
		err = c.ApplyMiddleware(d.cfg.middleware)

		if err != nil {
			log.WithFields(log.Fields{
				"mode":   d.cfg.mode,
				"error":  err.Error(),
				"host":   request.Host,
				"method": request.Method,
				"path":   request.URL.Path,
			}).Error("could not forward request, middleware failed to modify request.")
			return nil, err
		}

		request, err = c.reconstructRequest()
		if err != nil {
			return nil, err
		}
	}

	resp, err := d.http.Do(request)

	if err != nil {
		log.WithFields(log.Fields{
			"mode":   d.cfg.mode,
			"error":  err.Error(),
			"host":   request.Host,
			"method": request.Method,
			"path":   request.URL.Path,
		}).Error("could not forward request, failed to do an HTTP request.")
		return nil, err
	}

	log.WithFields(log.Fields{
		"mode":   d.cfg.mode,
		"host":   request.Host,
		"method": request.Method,
		"path":   request.URL.Path,
	}).Debug("response from external service got successfuly!")

	resp.Header.Set("hoverfly", "Was-Here")
	return resp, nil

}

// save gets request fingerprint, extracts request body, status code and headers, then saves it to cache
func (d *DBClient) save(req *http.Request, reqBody []byte, resp *http.Response, respBody []byte) {
	// record request here
	key := getRequestFingerprint(req, reqBody)

	if resp == nil {
		resp = emptyResp
	} else {
		responseObj := response{
			Status:  resp.StatusCode,
			Body:    string(respBody),
			Headers: resp.Header,
		}

		log.WithFields(log.Fields{
			"path":          req.URL.Path,
			"rawQuery":      req.URL.RawQuery,
			"requestMethod": req.Method,
			"bodyLen":       len(reqBody),
			"destination":   req.Host,
			"hashKey":       key,
		}).Debug("Capturing")

		requestObj := requestDetails{
			Path:        req.URL.Path,
			Method:      req.Method,
			Destination: req.Host,
			Scheme:      req.URL.Scheme,
			Query:       req.URL.RawQuery,
			Body:        string(reqBody),
			RemoteAddr:  req.RemoteAddr,
			Headers:     req.Header,
		}

		payload := Payload{
			Response: responseObj,
			Request:  requestObj,
			ID:       key,
		}

		bts, err := payload.encode()
		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error("Failed to serialize payload")
		} else {
			d.cache.Set([]byte(key), bts)
		}
	}
}

// getRequestFingerprint returns request hash
func getRequestFingerprint(req *http.Request, requestBody []byte) string {
	details := requestDetails{
		Path:        req.URL.Path,
		Method:      req.Method,
		Destination: req.Host,
		Query:       req.URL.RawQuery,
		Body:        string(requestBody),
	}

	r := request{details: details}
	return r.hash()
}

// getResponse returns stored response from cache
func (d *DBClient) getResponse(req *http.Request) *http.Response {

	if req.Body == nil {
		req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	reqBody, err := ioutil.ReadAll(req.Body)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error when reading request body")
	}

	key := getRequestFingerprint(req, reqBody)

	payloadBts, err := d.cache.Get([]byte(key))

	if err == nil {
		// getting cache response
		payload, err := decodePayload(payloadBts)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
				"value": string(payloadBts),
				"key":   key,
			}).Error("Failed to decode payload")
			return hoverflyError(req, err, "Failed to virtualize", http.StatusInternalServerError)
		}

		c := NewConstructor(req, *payload)

		if d.cfg.middleware != "" {
			_ = c.ApplyMiddleware(d.cfg.middleware)
		}

		response := c.reconstructResponse()

		log.WithFields(log.Fields{
			"key":         key,
			"mode":        "virtualize",
			"middleware":  d.cfg.middleware,
			"path":        req.URL.Path,
			"rawQuery":    req.URL.RawQuery,
			"method":      req.Method,
			"destination": req.Host,
			"status":      payload.Response.Status,
			"bodyLength":  response.ContentLength,
		}).Info("Response found, returning")

		return response

	}

	log.WithFields(log.Fields{
		"key":         key,
		"error":       err.Error(),
		"query":       req.URL.RawQuery,
		"path":        req.URL.RawPath,
		"destination": req.Host,
		"method":      req.Method,
	}).Warn("Failed to retrieve response from cache")
	// return error? if we return nil - proxy forwards request to original destination
	return hoverflyError(req, err, "Could not find recorded request, please record it first!", http.StatusPreconditionFailed)
}

// modifyRequestResponse modifies outgoing request and then modifies incoming response, neither request nor response
// is saved to cache.
func (d *DBClient) modifyRequestResponse(req *http.Request, middleware string) (*http.Response, error) {

	// modifying request
	resp, err := d.doRequest(req)

	if err != nil {
		return nil, err
	}

	// preparing payload
	bodyBytes, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		log.WithFields(log.Fields{
			"error":      err.Error(),
			"middleware": middleware,
		}).Error("Failed to read response body after sending modified request")
		return nil, err
	}

	r := response{
		Status:  resp.StatusCode,
		Body:    string(bodyBytes),
		Headers: resp.Header,
	}
	payload := Payload{Response: r}

	c := NewConstructor(req, payload)
	// applying middleware to modify response
	err = c.ApplyMiddleware(middleware)

	if err != nil {
		return nil, err
	}

	newResponse := c.reconstructResponse()

	log.WithFields(log.Fields{
		"status":      newResponse.StatusCode,
		"middleware":  middleware,
		"mode":        "modify",
		"path":        c.payload.Request.Path,
		"rawQuery":    c.payload.Request.Query,
		"method":      c.payload.Request.Method,
		"destination": c.payload.Request.Destination,
		// original here
		"originalPath":        req.URL.Path,
		"originalRawQuery":    req.URL.RawQuery,
		"originalMethod":      req.Method,
		"originalDestination": req.Host,
	}).Info("request and response modified, returning")

	return newResponse, nil

}
<|endoftext|>


<commit_before>package monico

import (
	"os"
	"time"
)

type Moniter struct {
	path        string
	lastModTime time.Time
}

func NewMoniter(path string) (*Moniter, error) {
	info, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	return &Moniter{
		path:        path,
		lastModTime: info.ModTime(),
	}, nil
}

func NewMoniterWithWD() (*Moniter, error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	return NewMoniter(wd)
}

func (m *Moniter) Path() string {
	return m.path
}

func (m *Moniter) Modified() (bool, error) {
	info, err := os.Stat(m.path)
	if err != nil {
		return false, err
	}
	return !m.lastModTime.Equal(info.ModTime()), nil
}
<commit_msg>Implement LastModTime<commit_after>package monico

import (
	"os"
	"time"
)

type Moniter struct {
	path        string
	lastModTime time.Time
}

func NewMoniter(path string) (*Moniter, error) {
	info, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	return &Moniter{
		path:        path,
		lastModTime: info.ModTime(),
	}, nil
}

func NewMoniterWithWD() (*Moniter, error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	return NewMoniter(wd)
}

func (m *Moniter) Path() string {
	return m.path
}

func (m *Moniter) Modified() (bool, error) {
	info, err := os.Stat(m.path)
	if err != nil {
		return false, err
	}
	return !m.lastModTime.Equal(info.ModTime()), nil
}

func (m *Moniter) LastModTime() time.Time {
	return m.lastModTime
}
<|endoftext|>


<commit_before>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

/*
 * Copyright (c) 2011 Alexander Færøy <ahf@0x90.dk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

type ChannelRegistry struct {
    ircd *Ircd // Pointer to the IRCd instance.

    // Map channel names to Channels
    channels map[string] *Channel
}

func NewChannelRegistry(ircd *Ircd) *ChannelRegistry {
    return &ChannelRegistry{ircd, make(map[string] *Channel)}
}

func (this *ChannelRegistry) Find(name string) *Channel {
    if channel, ok := this.channels[ToLower(name)]; ok {
        return channel
    }

    return nil
}

func (this *ChannelRegistry) FindOrCreate(name string) *Channel {
    lowered := ToLower(name)
    channel := this.Find(lowered)

    if channel == nil {
        // Don't use ToLower() here.
        channel = NewChannel(this.ircd, name)

        // But use it here.
        this.channels[lowered] = channel
    }

    return channel
}

func (this *ChannelRegistry) Unregister(channel *Channel) {
    this.Printf("Unregistering Channel", channel)
    this.channels[ToLower(channel.Name())] = nil, false
}

func (this *ChannelRegistry) Printf(format string, a...interface{}) {
    this.ircd.Printf(format, a...)
}
<commit_msg>Remove unused parameter.<commit_after>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

/*
 * Copyright (c) 2011 Alexander Færøy <ahf@0x90.dk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

type ChannelRegistry struct {
    ircd *Ircd // Pointer to the IRCd instance.

    // Map channel names to Channels
    channels map[string] *Channel
}

func NewChannelRegistry(ircd *Ircd) *ChannelRegistry {
    return &ChannelRegistry{ircd, make(map[string] *Channel)}
}

func (this *ChannelRegistry) Find(name string) *Channel {
    if channel, ok := this.channels[ToLower(name)]; ok {
        return channel
    }

    return nil
}

func (this *ChannelRegistry) FindOrCreate(name string) *Channel {
    lowered := ToLower(name)
    channel := this.Find(lowered)

    if channel == nil {
        // Don't use ToLower() here.
        channel = NewChannel(this.ircd, name)

        // But use it here.
        this.channels[lowered] = channel
    }

    return channel
}

func (this *ChannelRegistry) Unregister(channel *Channel) {
    this.Printf("Unregistering Channel")
    this.channels[ToLower(channel.Name())] = nil, false
}

func (this *ChannelRegistry) Printf(format string, a...interface{}) {
    this.ircd.Printf(format, a...)
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func TestCreateLogFile(t *testing.T) {
	testDir := "_test"
	tmpDir := filepath.Join(testDir, "tmp")
	testFile := filepath.Join(tmpDir, "logfile")

	if err := os.MkdirAll(tmpDir, 0755); err != nil {
		t.Fatal(err)
	}

	Convey("Given a path that already exists", t, func() {
		So(os.Remove(testFile), ShouldBeNil)
		// Chmod it to something other than -rw-rw-rw-
		So(ioutil.WriteFile(testFile, []byte("foo"), 0777), ShouldBeNil)

		Convey("Then chmod the file to -rw-rw-rw-", func() {
			f, err := createLogFile(testFile)
			So(err, ShouldBeNil)
			defer f.Close()

			fi, err := f.Stat()
			So(err, ShouldBeNil)
			So(fi.Mode().String(), ShouldEqual, "-rw-rw-rw-")
		})

		Convey("Then do not replace the file", func() {
			f, err := createLogFile(testFile)
			So(err, ShouldBeNil)
			f.Close()

			b, err := ioutil.ReadFile(testFile)
			So(err, ShouldBeNil)
			So(string(b), ShouldEqual, "foo")
		})
	})

	Convey("Given a path that does not exist", t, func() {
		if _, err := os.Stat(testFile); err == nil {
			So(os.Remove(testFile), ShouldBeNil)
		}

		Convey("Then create the file", func() {
			f, err := createLogFile(testFile)
			So(err, ShouldBeNil)
			f.Close()

			// If it doesn't exist, stat will fail with IsNotExist
			_, err = os.Stat(testFile)
			So(err, ShouldBeNil)
		})

		Convey("Then chmod the file to -rw-rw-rw-", func() {
			f, err := createLogFile(testFile)
			So(err, ShouldBeNil)
			defer f.Close()

			fi, err := f.Stat()
			So(err, ShouldBeNil)
			So(fi.Mode().String(), ShouldEqual, "-rw-rw-rw-")
		})
	})

	os.RemoveAll(testDir)
}
<commit_msg>install: use RemoveAll<commit_after>package main

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func TestCreateLogFile(t *testing.T) {
	testDir := "_test"
	tmpDir := filepath.Join(testDir, "tmp")
	testFile := filepath.Join(tmpDir, "logfile")

	if err := os.MkdirAll(tmpDir, 0755); err != nil {
		t.Fatal(err)
	}

	Convey("Given a path that already exists", t, func() {
		So(os.RemoveAll(testFile), ShouldBeNil)
		// Chmod it to something other than -rw-rw-rw-
		So(ioutil.WriteFile(testFile, []byte("foo"), 0777), ShouldBeNil)

		Convey("Then chmod the file to -rw-rw-rw-", func() {
			f, err := createLogFile(testFile)
			So(err, ShouldBeNil)
			defer f.Close()

			fi, err := f.Stat()
			So(err, ShouldBeNil)
			So(fi.Mode().String(), ShouldEqual, "-rw-rw-rw-")
		})

		Convey("Then do not replace the file", func() {
			f, err := createLogFile(testFile)
			So(err, ShouldBeNil)
			f.Close()

			b, err := ioutil.ReadFile(testFile)
			So(err, ShouldBeNil)
			So(string(b), ShouldEqual, "foo")
		})
	})

	Convey("Given a path that does not exist", t, func() {
		So(os.RemoveAll(testFile), ShouldBeNil)

		Convey("Then create the file", func() {
			f, err := createLogFile(testFile)
			So(err, ShouldBeNil)
			f.Close()

			// If it doesn't exist, stat will fail with IsNotExist
			_, err = os.Stat(testFile)
			So(err, ShouldBeNil)
		})

		Convey("Then chmod the file to -rw-rw-rw-", func() {
			f, err := createLogFile(testFile)
			So(err, ShouldBeNil)
			defer f.Close()

			fi, err := f.Stat()
			So(err, ShouldBeNil)
			So(fi.Mode().String(), ShouldEqual, "-rw-rw-rw-")
		})
	})

	os.RemoveAll(testDir)
}
<|endoftext|>